/* eslint-disable */
// Depends on jsbn.js and rng.js
// Version 1.1: support utf-8 encoding in pkcs1pad2
// convert a (hex) string to a bignum object
import { BigInteger, nbi, parseBigInt } from './jsbn';
import { SecureRandom } from './rng'; // function linebrk(s,n) {
//   var ret = "";
//   var i = 0;
//   while(i + n < s.length) {
//     ret += s.substring(i,i+n) + "\n";
//     i += n;
//   }
//   return ret + s.substring(i,s.length);
// }
// function byte2Hex(b) {
//   if(b < 0x10)
//     return "0" + b.toString(16);
//   else
//     return b.toString(16);
// }

function pkcs1pad1(s, n) {
    if (n < s.length + 22) {
        console.error('Message too long for RSA');
        return null;
    }

    var len = n - s.length - 6;
    var filler = '';

    for (var f = 0; f < len; f += 2) {
        filler += 'ff';
    }

    var m = '0001' + filler + '00' + s;
    return parseBigInt(m, 16);
} // PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint

function pkcs1pad2(s, n) {
    if (n < s.length + 11) {
        // TODO: fix for utf-8
        console.error('Message too long for RSA');
        return null;
    }

    var ba = [];
    var i = s.length - 1;

    while (i >= 0 && n > 0) {
        var c = s.charCodeAt(i--);

        if (c < 128) {
            // encode using utf-8
            ba[--n] = c;
        } else {
            if (c > 127 && c < 2048) {
                ba[--n] = (c & 63) | 128;
                ba[--n] = (c >> 6) | 192;
            } else {
                ba[--n] = (c & 63) | 128;
                ba[--n] = ((c >> 6) & 63) | 128;
                ba[--n] = (c >> 12) | 224;
            }
        }
    }

    ba[--n] = 0;
    var rng = new SecureRandom();
    var x = [];

    while (n > 2) {
        // random non-zero pad
        x[0] = 0;

        while (x[0] == 0) {
            rng.nextBytes(x);
        }

        ba[--n] = x[0];
    }

    ba[--n] = 2;
    ba[--n] = 0;
    return new BigInteger(ba);
} // "empty" RSA key constructor

var RSAKey =
    /** @class */
    (function () {
        function RSAKey() {
            this.n = null;
            this.e = 0;
            this.d = null;
            this.p = null;
            this.q = null;
            this.dmp1 = null;
            this.dmq1 = null;
            this.coeff = null;
        } //#region PROTECTED
        // protected
        // RSAKey.prototype.doPublic = RSADoPublic;
        // Perform raw public operation on "x": return x^e (mod n)

        RSAKey.prototype.doPublic = function (x) {
            return x.modPowInt(this.e, this.n);
        }; // RSAKey.prototype.doPrivate = RSADoPrivate;
        // Perform raw private operation on "x": return x^d (mod n)

        RSAKey.prototype.doPrivate = function (x) {
            if (this.p == null || this.q == null) {
                return x.modPow(this.d, this.n);
            } // TODO: re-calculate any missing CRT params

            var xp = x.mod(this.p).modPow(this.dmp1, this.p);
            var xq = x.mod(this.q).modPow(this.dmq1, this.q);

            while (xp.compareTo(xq) < 0) {
                xp = xp.add(this.p);
            }

            return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq);
        }; //#endregion PROTECTED
        //#region PUBLIC
        // RSAKey.prototype.setPublic = RSASetPublic;
        // Set the public key fields N and e from hex strings

        RSAKey.prototype.setPublic = function (N, E) {
            if (N != null && E != null && N.length > 0 && E.length > 0) {
                this.n = parseBigInt(N, 16);
                this.e = parseInt(E, 16);
            } else {
                console.error('Invalid RSA public key');
            }
        }; // RSAKey.prototype.encrypt = RSAEncrypt;
        // Return the PKCS#1 RSA encryption of "text" as an even-length hex string

        RSAKey.prototype.encrypt = function (text) {
            var maxLength = (this.n.bitLength() + 7) >> 3;
            var m = pkcs1pad2(text, maxLength);

            if (m == null) {
                return null;
            }

            var c = this.doPublic(m);

            if (c == null) {
                return null;
            }

            var h = c.toString(16);
            var length = h.length; // fix zero before result

            for (var i = 0; i < maxLength * 2 - length; i++) {
                h = '0' + h;
            }

            return h;
        }; // RSAKey.prototype.setPrivate = RSASetPrivate;
        // Set the private key fields N, e, and d from hex strings

        RSAKey.prototype.setPrivate = function (N, E, D) {
            if (N != null && E != null && N.length > 0 && E.length > 0) {
                this.n = parseBigInt(N, 16);
                this.e = parseInt(E, 16);
                this.d = parseBigInt(D, 16);
            } else {
                console.error('Invalid RSA private key');
            }
        }; // RSAKey.prototype.setPrivateEx = RSASetPrivateEx;
        // Set the private key fields N, e, d and CRT params from hex strings

        RSAKey.prototype.setPrivateEx = function (N, E, D, P, Q, DP, DQ, C) {
            if (N != null && E != null && N.length > 0 && E.length > 0) {
                this.n = parseBigInt(N, 16);
                this.e = parseInt(E, 16);
                this.d = parseBigInt(D, 16);
                this.p = parseBigInt(P, 16);
                this.q = parseBigInt(Q, 16);
                this.dmp1 = parseBigInt(DP, 16);
                this.dmq1 = parseBigInt(DQ, 16);
                this.coeff = parseBigInt(C, 16);
            } else {
                console.error('Invalid RSA private key');
            }
        }; // RSAKey.prototype.generate = RSAGenerate;
        // Generate a new random private key B bits long, using public expt E

        RSAKey.prototype.generate = function (B, E) {
            var rng = new SecureRandom();
            var qs = B >> 1;
            this.e = parseInt(E, 16);
            var ee = new BigInteger(E, 16);

            for (;;) {
                for (;;) {
                    this.p = new BigInteger(B - qs, 1, rng);

                    if (this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.p.isProbablePrime(10)) {
                        break;
                    }
                }

                for (;;) {
                    this.q = new BigInteger(qs, 1, rng);

                    if (this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.q.isProbablePrime(10)) {
                        break;
                    }
                }

                if (this.p.compareTo(this.q) <= 0) {
                    var t = this.p;
                    this.p = this.q;
                    this.q = t;
                }

                var p1 = this.p.subtract(BigInteger.ONE);
                var q1 = this.q.subtract(BigInteger.ONE);
                var phi = p1.multiply(q1);

                if (phi.gcd(ee).compareTo(BigInteger.ONE) == 0) {
                    this.n = this.p.multiply(this.q);
                    this.d = ee.modInverse(phi);
                    this.dmp1 = this.d.mod(p1);
                    this.dmq1 = this.d.mod(q1);
                    this.coeff = this.q.modInverse(this.p);
                    break;
                }
            }
        }; // RSAKey.prototype.decrypt = RSADecrypt;
        // Return the PKCS#1 RSA decryption of "ctext".
        // "ctext" is an even-length hex string and the output is a plain string.

        RSAKey.prototype.decrypt = function (ctext) {
            var c = parseBigInt(ctext, 16);
            var m = this.doPrivate(c);

            if (m == null) {
                return null;
            }

            return pkcs1unpad2(m, (this.n.bitLength() + 7) >> 3);
        }; // Generate a new random private key B bits long, using public expt E

        RSAKey.prototype.generateAsync = function (B, E, callback) {
            var rng = new SecureRandom();
            var qs = B >> 1;
            this.e = parseInt(E, 16);
            var ee = new BigInteger(E, 16);
            var that = this; // These functions have non-descript names because they were originally for(;;) loops.
            // I don't know about cryptography to give them better names than loop1-4.

            var loop1 = function () {
                var loop4 = function () {
                    if (that.p.compareTo(that.q) <= 0) {
                        var t = that.p;
                        that.p = that.q;
                        that.q = t;
                    }

                    var p1 = that.p.subtract(BigInteger.ONE);
                    var q1 = that.q.subtract(BigInteger.ONE);
                    var phi = p1.multiply(q1);

                    if (phi.gcd(ee).compareTo(BigInteger.ONE) == 0) {
                        that.n = that.p.multiply(that.q);
                        that.d = ee.modInverse(phi);
                        that.dmp1 = that.d.mod(p1);
                        that.dmq1 = that.d.mod(q1);
                        that.coeff = that.q.modInverse(that.p);
                        setTimeout(function () {
                            callback();
                        }, 0); // escape
                    } else {
                        setTimeout(loop1, 0);
                    }
                };

                var loop3 = function () {
                    that.q = nbi();
                    that.q.fromNumberAsync(qs, 1, rng, function () {
                        that.q.subtract(BigInteger.ONE).gcda(ee, function (r) {
                            if (r.compareTo(BigInteger.ONE) == 0 && that.q.isProbablePrime(10)) {
                                setTimeout(loop4, 0);
                            } else {
                                setTimeout(loop3, 0);
                            }
                        });
                    });
                };

                var loop2 = function () {
                    that.p = nbi();
                    that.p.fromNumberAsync(B - qs, 1, rng, function () {
                        that.p.subtract(BigInteger.ONE).gcda(ee, function (r) {
                            if (r.compareTo(BigInteger.ONE) == 0 && that.p.isProbablePrime(10)) {
                                setTimeout(loop3, 0);
                            } else {
                                setTimeout(loop2, 0);
                            }
                        });
                    });
                };

                setTimeout(loop2, 0);
            };

            setTimeout(loop1, 0);
        };

        RSAKey.prototype.sign = function (text, digestMethod, digestName) {
            var header = getDigestHeader(digestName);
            var digest = header + digestMethod(text).toString();
            var m = pkcs1pad1(digest, this.n.bitLength() / 4);

            if (m == null) {
                return null;
            }

            var c = this.doPrivate(m);

            if (c == null) {
                return null;
            }

            var h = c.toString(16);

            if ((h.length & 1) == 0) {
                return h;
            } else {
                return '0' + h;
            }
        };

        RSAKey.prototype.verify = function (text, signature, digestMethod) {
            var c = parseBigInt(signature, 16);
            var m = this.doPublic(c);

            if (m == null) {
                return null;
            }

            var unpadded = m.toString(16).replace(/^1f+00/, '');
            var digest = removeDigestHeader(unpadded);
            return digest == digestMethod(text).toString();
        };

        return RSAKey;
    })();

export { RSAKey }; // Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext

function pkcs1unpad2(d, n) {
    var b = d.toByteArray();
    var i = 0;

    while (i < b.length && b[i] == 0) {
        ++i;
    }

    if (b.length - i != n - 1 || b[i] != 2) {
        return null;
    }

    ++i;

    while (b[i] != 0) {
        if (++i >= b.length) {
            return null;
        }
    }

    var ret = '';

    while (++i < b.length) {
        var c = b[i] & 255;

        if (c < 128) {
            // utf-8 decode
            ret += String.fromCharCode(c);
        } else {
            if (c > 191 && c < 224) {
                ret += String.fromCharCode(((c & 31) << 6) | (b[i + 1] & 63));
                ++i;
            } else {
                ret += String.fromCharCode(((c & 15) << 12) | ((b[i + 1] & 63) << 6) | (b[i + 2] & 63));
                i += 2;
            }
        }
    }

    return ret;
} // https://tools.ietf.org/html/rfc3447#page-43

var DIGEST_HEADERS = {
    md2: '3020300c06082a864886f70d020205000410',
    md5: '3020300c06082a864886f70d020505000410',
    sha1: '3021300906052b0e03021a05000414',
    sha224: '302d300d06096086480165030402040500041c',
    sha256: '3031300d060960864801650304020105000420',
    sha384: '3041300d060960864801650304020205000430',
    sha512: '3051300d060960864801650304020305000440',
    ripemd160: '3021300906052b2403020105000414'
};

function getDigestHeader(name) {
    return DIGEST_HEADERS[name] || '';
}

function removeDigestHeader(str) {
    for (var name_1 in DIGEST_HEADERS) {
        if (DIGEST_HEADERS.hasOwnProperty(name_1)) {
            var header = DIGEST_HEADERS[name_1];
            var len = header.length;

            if (str.substr(0, len) == header) {
                return str.substr(len);
            }
        }
    }

    return str;
} // Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string
// function RSAEncryptB64(text) {
//  var h = this.encrypt(text);
//  if(h) return hex2b64(h); else return null;
// }
// public
// RSAKey.prototype.encrypt_b64 = RSAEncryptB64;
